
#ifndef __HTTP_CONNECTION_H__
#define __HTTP_CONNECTION_H__

#include "../HTTP.h"
#include "../knsock.h"
#include "../knutil.h"
#include "Status.h"

#include <string>

namespace HTTP {

class Connection
{

public:
	// Bad code
	// Bad names
	Connection(const std::string& v_host): host(v_host), connected(false), status(0)
	{
		http_connect();
	}

	~Connection()
	{
		http_close();
	}

/*
	// Note
	// Unimplemented
	// Is it usefull?
	void request(const std::string& method, const std::string& file, const std::string& version="HTTP/1.1")
	{
	}
*/

	void request(const RequestObject& req)
	{
		request(req.to_string());
	}

	void request(const std::string& request_data)
	{
		send(sockfd, request_data.c_str(), request_data.length(), 0);
	}

	void get_response(ResponseObject& resp)
	{
		int len;
		char buffer[8192];
		while((len = recv(sockfd, buffer, sizeof(buffer), 0)) > 0)
		{
			resp.write(buffer, len);
		}
		resp.close();
	}

	bool is_ok() const
	{
		return status != -1;
	}

	std::string get_error() const
	{
		return error_line;
	}

private:
	SOCKET sockfd;
	bool connected;
	std::string host;
	std::string error_line;
	int status;

	Connection(const Connection&);

	std::string get_hostname()
	{
		std::string::size_type i = host.find(':');
		if( i==std::string::npos )
		{
			return host;
		}
		return host.substr(0, i);
	}

	int get_port()
	{
		std::string::size_type i = host.find(':');
		if( i==std::string::npos || i==host.length()-1 )
		{
			return 0;
		}
		return knlib::util::str_cast<int>(host.substr(i+1));
	}

	void http_connect()
	{
		int n;
		struct sockaddr_in address;
		struct hostent *hostinfo;
		struct servent *servinfo;

	//	hostinfo = gethostbyname(host.c_str());
		hostinfo = gethostbyname(get_hostname().c_str());
		if(!hostinfo)
		{
			error_line = "Bad host";
			status = -1;
			return;
		}

		servinfo = getservbyname("http", "tcp");
		if(!servinfo)
		{
			error_line = "No http service";
			status = -1;
			return;
		}

		int port = get_port();
		if(port==0)
		{
			port = 80;
		}

		sockfd = socket(AF_INET, SOCK_STREAM, 0);

		address.sin_family = AF_INET;
	//	address.sin_port = servinfo->s_port;
		address.sin_port = htons(port);
		address.sin_addr = *(struct in_addr*)*hostinfo->h_addr_list;
		n = sizeof(address);

		n = connect(sockfd, (struct sockaddr *)&address, n);
		if(n==-1)
		{
			error_line = "Can't connect host";
			status = -1;
			return;
		}
		
		connected = true;

	}

	void http_close()
	{
		if(connected)
		{
			CLOSESOCKET(sockfd);
			connected = false;
		}
	}
	
};


void single_request(const std::string& host, const std::string& request_data, ResponseObject& resp)
{
	Connection con(host);
	if(!con.is_ok())
	{
		resp.set_error(con.get_error());
		return;
	}
	con.request(request_data);
	con.get_response(resp);
}

void single_request(const RequestObject& req, ResponseObject& resp)
{
	Connection con(req.host());
	if(!con.is_ok())
	{
		resp.set_error(con.get_error());
		return;
	}
	con.request(req);
	con.get_response(resp);
}

void redirectable_request(RequestObject req, ResponseObject& resp)
{
	single_request(req, resp);
	for(int i = 0; Status::is_redirect(resp.status_code()) && i < 8; i++)
	{
		std::string nurl = resp.get_header("Location");
		if(nurl=="")
			return;
		req.change_url(nurl);
		resp.clear();
		single_request(req, resp);
	}
}



}//end namespace HTTP


#endif

